home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Developer Toolbox 6.1
/
SGI Developer Toolbox 6.1 - Disc 4.iso
/
src
/
demos
/
GL
/
libdemo
/
ui.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-08-01
|
11KB
|
505 lines
/*
* Copyright 1991, 1992, 1993, 1994, Silicon Graphics, Inc.
* All Rights Reserved.
*
* This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
* the contents of this file may not be disclosed to third parties, copied or
* duplicated in any form, in whole or in part, without the prior written
* permission of Silicon Graphics, Inc.
*
* RESTRICTED RIGHTS LEGEND:
* Use, duplication or disclosure by the Government is subject to restrictions
* as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
* and Computer Software clause at DFARS 252.227-7013, and/or in similar or
* successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
* rights reserved under the Copyright Laws of the United States.
*/
/*
* A function (ui) taking one argument, which is a pointer to a
* function to draw a 3-dimensional scene. The function will be
* passed an incremental quaternion rotation, and an incremental
* translation, and should redraw the object in the new orientation
* when called.
* Implemented by Gavin Bell, lots of ideas from Thant Tessman and
* the August '88 issue of Siggraph's "Computer Graphics," pp. 121-129.
*
* See 'ui.h' for visible programmers interface.
*/
#include <stdio.h>
#include <math.h>
#include <gl/gl.h>
#include <gl/device.h>
#include "ui.h"
/* Externally visible state: */
/* Is the user currently using the interface? */
int ui_quiet = TRUE;
int ui_noisy = FALSE;
/* States global to only this file */
static int exitflag = FALSE; /* Becomes 1 when user quits */
static int activeflag = TRUE; /* User interface active */
static int rotateflag = FALSE; /* When rotating with middle mouse */
static int zoomflag = FALSE; /* Left AND Middle, zoom */
static int panflag = FALSE; /* Left mouse, pan */
static int sbflag = FALSE; /* Getting spaceball events */
/*
* We mustn't call the user's redraw-the-scene function every time an
* event occurs, or the event queue may overflow. Therefore, these
* two flags are used for two update functions, one of which is active
* when the mouse is generating input (a mouse button is down) and one
* when the spaceball is generating input (the spaceball sends an
* all-zero event when it is done). If both are true at the same
* time, then the user's function will be called twice, and the two
* devices will effectively battle it out. No biggie.
*/
static int mouse_noisy = FALSE; /* Interacting with mouse */
static int spaceball_noisy = FALSE; /* Interacting with spaceball */
/*
* Window dimensions, used to convert mouse-clicks into a more
* convenient coordinate system.
*/
static int sizex, sizey, origx, origy;
/*
* Function prototypes for the functions local to this file
*/
void ui_init(void), ui_mouseupdate(void);
void ui_sbupdate(void);
void ui_to_worldspace(short, short, float *, float *);
void ui_zoom(void), ui_pan(void), ui_redraw(void);
void ui_lmdown(void), ui_lmup(void);
void ui_mmdown(void), ui_mmup(void);
void ui_SBevent(long, short);
void ui_mousemotion();
void ui_schedulemouse();
/*
* Used to remember what function was passed
*/
static void (*user_fn)(float *, float*);
/*
* And now....
* The routines.
*
* This is the mail loop; it will call the supplied function (taking
* two float * arguments) when necessary to update the object
* rotations/translations and redraw the scene.
*/
void
ui(void (*fn)(float *, float *))
{
static int initialized = 0; /* Initialized yet? */
user_fn = fn;
if (!initialized)
{
ui_init();
initialized = 1;
}
while (exitflag == 0)
{
/* All the action occurs in response to add_updates */
event();
}
}
static void
ui_init()
{
long gid;
gid = winget();
getsize(&sizex, &sizey); /* Gotta know where center of */
getorigin(&origx, &origy); /* window is */
if (activeflag)
{
qdevice(REDRAW); /* Keep track of window size changes */
qdevice(LEFTMOUSE);
qdevice(MIDDLEMOUSE);
qdevice(CURSORX);
qdevice(CURSORY);
add_event(ANY, REDRAW, gid, ui_redraw, NULL);
add_event(gid, LEFTMOUSE, DOWN, ui_lmdown, NULL);
add_event(gid, LEFTMOUSE, UP, ui_lmup, NULL);
add_event(gid, MIDDLEMOUSE, DOWN, ui_mmdown, NULL);
add_event(gid, MIDDLEMOUSE, UP, ui_mmup, NULL);
add_event(gid, CURSORX, ANY, ui_schedulemouse, NULL);
add_event(gid, CURSORY, ANY, ui_schedulemouse, NULL);
add_update(&mouse_noisy, ui_mouseupdate, NULL);
add_event(gid, SBTX, ANY, ui_SBevent, (void *)SBTX);
add_event(gid, SBTY, ANY, ui_SBevent, (void *)SBTY);
add_event(gid, SBTZ, ANY, ui_SBevent, (void *)SBTZ);
add_event(gid, SBRX, ANY, ui_SBevent, (void *)SBRX);
add_event(gid, SBRY, ANY, ui_SBevent, (void *)SBRY);
add_event(gid, SBRZ, ANY, ui_SBevent, (void *)SBRZ);
add_event(gid, SBPERIOD, ANY, ui_SBevent, (void *)SBPERIOD);
qdevice(SBTX);
qdevice(SBTY);
qdevice(SBTZ);
qdevice(SBRX);
qdevice(SBRY);
qdevice(SBRZ);
qdevice(SBPERIOD);
add_update(&spaceball_noisy, ui_sbupdate, NULL);
}
}
/*
* These keep track of the mouse position on the screen. They are
* initialized to (-1) so we can tell when the user first starts
* interacting. They are reset to (-1) when the mouse buttons go up.
*/
static short omx= (-1), omy = (-1), nmx = (-1), nmy = (-1);
/*
* This function is repeatedly called as the user manipulates the
* mouse. It is only called if the mouse moves, however.
*/
static void
ui_mouseupdate()
{
nmx = getvaluator(CURSORX);
if (omx == (-1)) omx = nmx;
nmy = getvaluator(CURSORY);
if (omy == (-1)) omy = nmy;
if (panflag)
ui_pan();
else if (zoomflag)
ui_zoom();
else if (rotateflag)
{
float p1x, p1y, p2x, p2y ;
float r[4], t[3] ;
vzero(t);
ui_to_worldspace(omx-origx, omy-origy, &p1x, &p1y);
ui_to_worldspace(nmx-origx, nmy-origy, &p2x, &p2y);
trackball(r, p1x, p1y, p2x, p2y);
(*user_fn)(r, t);
}
omx = nmx; omy = nmy;
mouse_noisy = FALSE; /* Update done */
}
static void
ui_schedulemouse()
{
mouse_noisy = TRUE;
}
/*
* Map mouse click sx, sy to a more convenient (-1.0,1.0)
* range, based on window size.
*/
static void
ui_to_worldspace(short sx, short sy, float * wx, float * wy)
{
(*wx) = (2.0 * sx) / (float) sizex - 1.0;
(*wy) = (2.0 * sy) / (float) sizey - 1.0;
}
/*
* Zoom in/out; a translation of 1.0 is equal to a full sweep across
* the window-- the user's function must scale accordingly.
*/
static void
ui_zoom()
{
float r[4], t[3];
vzero(r); r[3] = 1.0;
vzero(t);
t[2] = (float)(nmx-omx)/(float)sizex +
(float)(nmy-omy)/(float)sizey;
(*user_fn)(r, t);
}
/*
* Translate in xy plane. The window is assumed to be unit-sized in
* the x and y directions; the values returned must be scaled
* accordingly.
*/
static void
ui_pan()
{
float r[4], t[3];
vzero(r); r[3] = 1.0;
vset(t, (float)(nmx-omx)/(float)sizex,
(float)(nmy-omy)/(float)sizey, 0.0);
(*user_fn)(r, t);
}
/*
* Called in case of REDRAW events to keep track of window size.
*/
static void
ui_redraw()
{
reshapeviewport();
getsize(&sizex, &sizey);
getorigin(&origx, &origy);
}
void
ui_exit()
{
exitflag = 1;
}
static void
figure_ui_noisy()
{
ui_noisy = zoomflag | panflag | rotateflag | sbflag;
ui_quiet = !ui_noisy;
}
void
ui_active(int flag)
{
activeflag = flag ;
if (!flag)
{
zoomflag = panflag = rotateflag = sbflag = FALSE;
figure_ui_noisy();
}
}
static void
ui_lmdown()
{
if (activeflag)
{
if (rotateflag == TRUE)
{
zoomflag = TRUE;
rotateflag = FALSE;
}
else
{
panflag = TRUE;
mouse_noisy = TRUE;
figure_ui_noisy();
}
}
}
static void
ui_lmup()
{
if (activeflag)
{
if (zoomflag == TRUE)
{
zoomflag = FALSE;
rotateflag = TRUE;
}
else
{
panflag = FALSE;
mouse_noisy = FALSE;
figure_ui_noisy();
omx = omy = nmx = nmy = (-1);
}
}
}
static void
ui_mmdown()
{
if (activeflag)
{
if (panflag == TRUE)
{
zoomflag = TRUE;
panflag = FALSE;
}
else
{
rotateflag = TRUE;
mouse_noisy = TRUE;
figure_ui_noisy();
}
}
}
static void
ui_mmup()
{
if (activeflag)
{
if (zoomflag == TRUE)
{
zoomflag = FALSE;
panflag = TRUE;
}
else
{
rotateflag = FALSE;
mouse_noisy = FALSE;
figure_ui_noisy();
omx = omy = nmx = nmy = (-1);
}
}
}
/*
*---------------------------
* Support for the SpaceBall
*---------------------------
*/
/*
* SBaxistoquat will take an axis rotation vector and create an equivalent
* quaternion.
*/
static void
SBaxistoquat(float scale, float rvec[3], float q[4])
{
float rads;
/* Find the length of the vector */
rads = sqrt(rvec[0]*rvec[0] + rvec[1]*rvec[1] + rvec[2]*rvec[2]);
/* If the vector has zero length - return the identity matrix */
if (fabs(rads) < 0.001)
{
q[0] = 0.0;
q[1] = 0.0;
q[2] = 0.0;
q[3] = 1.0;
return;
}
axis_to_quat(rvec, rads*scale/2.0, q);
}
static float t_rate = 0.00000005;
static float r_rate = 0.0000005;
/*
* These keep track of the spaceball translation and rotation.
*/
static float txyz[3], rxyz[3], period;
static void
ui_sbupdate()
{
float tran[3], rot[4];
tran[0] = t_rate * period * txyz[0];
tran[1] = t_rate * period * txyz[1];
tran[2] = t_rate * period * txyz[2];
SBaxistoquat(-r_rate * period, rxyz, rot);
(*user_fn)(rot, tran);
spaceball_noisy = FALSE; /* Update done */
}
/*
* Translate spaceball events into rotations/translations.
* This is a very simplistic way of handling the spaceball.
*
* Note: The spaceball seems to be in a left-handed coordinate system;
* hence the negatives below. Then again, my math might be backwards
* (it has happened before!).
*
*/
static void
ui_SBevent(long event, short val)
{
static int all_zero = 0;
static int have_all = 0;
int bit;
switch(event)
{
case SBTX:
txyz[0] = (float)val;
bit = 0x1;
break;
case SBTY:
txyz[1] = (float)val;
bit = 0x2;
break;
case SBTZ:
txyz[2] = -(float)val;
bit = 0x4;
break;
case SBRX:
rxyz[0] = (float)val;
bit = 0x8;
break;
case SBRY:
rxyz[1] = (float)val;
bit = 0x10;
break;
case SBRZ:
rxyz[2] = -(float)val;
bit = 0x20;
break;
case SBPERIOD:
period = (float)val;
bit = 0x40;
break;
}
if (val == 0) all_zero |= bit;
/*
* have_all lets us figure out when we've got all 7 spaceball
* events, and can go ahead and redraw.
*/
have_all |= bit;
if (have_all == 0x7F)
{
/*
* If all spaceball events were zero, then the spaceball is no
* longer being interacted with. If we had noticed that we
* were interacting, notice that we aren't any more!
*/
if ((all_zero == 0x3F) && sbflag)
{
spaceball_noisy = FALSE;
sbflag = FALSE;
figure_ui_noisy();
/*
* Let ui_sbupdate call the user's function to let them
* the spaceball has stopped moving.
*/
ui_sbupdate();
}
/*
* If they were not all zero, and we haven't already noticed
* that we are getting spaceball events, notice!
*/
if (all_zero != 0x3F)
{
spaceball_noisy = TRUE; /* Turn update on */
sbflag = TRUE;
figure_ui_noisy();
}
have_all = 0;
all_zero = 0;
}
}